package com.yunkss.eftool.crypto.operator;

import com.yunkss.eftool.crypto.key.CisCodec;
import com.yunkss.eftool.crypto.key.SM2KeyGenerator;
import com.yunkss.eftool.crypto.params.SmProvider;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.stereotype.Component;

import java.security.SecureRandom;

/**
 * @author csx
 * @date 2022/2/18 8:47
 * @desc
 */

@Component
public class EftoolSM2Operator extends SmProvider {

    public EftoolSM2Operator() {
    }

    private ECPrivateKeyParameters getDefaultPrivateKey(String privateKeyHexStr) {
        return SM2KeyGenerator.getSM2PrivateKey(privateKeyHexStr);
    }

    private ECPublicKeyParameters getDefaultPublicKey(String publicKeyHexStr) {
        return SM2KeyGenerator.getSM2PublicKey(publicKeyHexStr);
    }

    public String encrypt(String plainText, ECPublicKeyParameters publicKey) throws InvalidCipherTextException {
        SM2Engine encryptSM2Engine = this.getSM2Engine(DEFAULT_MODE, publicKey, true);
        byte[] bytes = plainText.getBytes();
        return Hex.toHexString(encryptSM2Engine.processBlock(bytes, 0, bytes.length));
    }

    public String encrypt(String plainText, String publicKeyHexStr) throws InvalidCipherTextException {
        return this.encrypt(plainText, this.getDefaultPublicKey(publicKeyHexStr));
    }

    public String encrypt(SM2Engine.Mode mode, String plainText, ECPublicKeyParameters publicKey) throws InvalidCipherTextException {
        SM2Engine encryptSM2Engine = this.getSM2Engine(mode, publicKey, true);
        byte[] bytes = plainText.getBytes();
        return Hex.toHexString(encryptSM2Engine.processBlock(bytes, 0, bytes.length));
    }

    public String decrypt(String encryptHex, ECPrivateKeyParameters privateKey) throws InvalidCipherTextException {
        SM2Engine sm2Engine = this.getSM2Engine(DEFAULT_MODE, privateKey, false);
        byte[] hex = Hex.decode(encryptHex);
        byte[] bytes = sm2Engine.processBlock(hex, 0, hex.length);
        return new String(bytes);
    }

    public String decrypt(String encryptHex, String privateKeyHexStr) throws InvalidCipherTextException {
        return this.decrypt(encryptHex, this.getDefaultPrivateKey(privateKeyHexStr));
    }

    public String decrypt(SM2Engine.Mode mode, String encryptHex, ECPrivateKeyParameters privateKey) throws InvalidCipherTextException {
        SM2Engine sm2Engine = this.getSM2Engine(mode, privateKey, false);
        byte[] hex = Hex.decode(encryptHex);
        byte[] bytes = sm2Engine.processBlock(hex, 0, hex.length);
        return new String(bytes);
    }

    public String sign(ECPrivateKeyParameters privateKey, byte[] withId, byte[] bytes) throws CryptoException {
        SM2Signer signer = this.getSM2Signer(privateKey, withId);
        signer.update(bytes, 0, bytes.length);
        byte[] signature = signer.generateSignature();
        return Hex.toHexString(signature);
    }

    public String sign(ECPrivateKeyParameters privateKey, byte[] bytes) throws CryptoException {
        SM2Signer signer = this.getSM2Signer((ECPrivateKeyParameters) privateKey, (byte[]) null);
        signer.update(bytes, 0, bytes.length);
        byte[] signature = signer.generateSignature();
        return Hex.toHexString(signature);
    }

    public String sign(byte[] bytes, String privateKeyHexStr) throws CryptoException {
        SM2Signer signer = this.getSM2Signer((ECPrivateKeyParameters) this.getDefaultPrivateKey(privateKeyHexStr), (byte[]) null);
        signer.update(bytes, 0, bytes.length);
        byte[] signature = signer.generateSignature();
        return Hex.toHexString(signature);
    }

    public String sign(String str, String privateKeyHexStr) throws CryptoException {
        byte[] bytes = str.getBytes();
        SM2Signer signer = this.getSM2Signer((ECPrivateKeyParameters) this.getDefaultPrivateKey(privateKeyHexStr), (byte[]) null);
        signer.update(bytes, 0, bytes.length);
        byte[] signature = signer.generateSignature();
        return Hex.toHexString(signature);
    }

    public boolean verify(ECPublicKeyParameters pubKeyParameters, byte[] withId, byte[] plainText, byte[] sign) {
        SM2Signer signer = this.getSM2Signer(pubKeyParameters, withId);
        signer.update(plainText, 0, plainText.length);
        return signer.verifySignature(sign);
    }

    public boolean verify(ECPublicKeyParameters pubKeyParameters, byte[] plainText, byte[] sign) {
        SM2Signer signer = this.getSM2Signer((ECPublicKeyParameters) pubKeyParameters, (byte[]) null);
        signer.update(plainText, 0, plainText.length);
        return signer.verifySignature(sign);
    }

    public boolean verify(byte[] plainText, byte[] sign, String publicKeyHexStr) {
        SM2Signer signer = this.getSM2Signer((ECPublicKeyParameters) this.getDefaultPublicKey(publicKeyHexStr), (byte[]) null);
        signer.update(plainText, 0, plainText.length);
        return signer.verifySignature(sign);
    }

    public boolean verify(String plainText, String hexSign, String publicKeyHexStr) {
        byte[] bytes = plainText.getBytes();
        byte[] sign = CisCodec.decodeFromHexString(hexSign);
        return this.verify(bytes, sign, publicKeyHexStr);
    }

    private SM2Signer getSM2Signer(ECPublicKeyParameters pubKeyParameters, byte[] withId) {
        SM2Signer signer = new SM2Signer();
        Object param;
        if (withId != null) {
            param = new ParametersWithID(pubKeyParameters, withId);
        } else {
            param = pubKeyParameters;
        }

        signer.init(false, (CipherParameters) param);
        return signer;
    }

    private SM2Signer getSM2Signer(ECPrivateKeyParameters privateKey, byte[] withId) {
        SM2Signer signer = new SM2Signer();
        CipherParameters param = null;
        ParametersWithRandom pwr = new ParametersWithRandom(privateKey, new SecureRandom());
        if (withId != null) {
            param = new ParametersWithID(pwr, withId);
        } else {
            param = pwr;
        }

        signer.init(true, (CipherParameters) param);
        return signer;
    }

    private SM2Engine getSM2Engine(SM2Engine.Mode mode, ECKeyParameters keyParameters, boolean isEncrypt) {
        SM2Engine engine = new SM2Engine(mode);
        if (isEncrypt) {
            ParametersWithRandom pwr = new ParametersWithRandom(keyParameters, new SecureRandom());
            engine.init(true, pwr);
        } else {
            engine.init(false, keyParameters);
        }

        return engine;
    }

    public static void main(String[] args) throws InvalidCipherTextException {
        EftoolSM2Operator encryptUtils = new EftoolSM2Operator();
        String sm4Key = encryptUtils.encrypt("dedd2f09c3b99354c46a605598947d3d138fbbafcb53f9a12590e9bbc58169e2", "04ba3bc3c5834d9ad1a7d81c4b49cf1209d2f28e4a97c73b75d6561792a2bfafe811e0284692006b0ce1b51f8aadfd65241d80eb979365048253408f5d705ec17b");
        //3081aa022100a62fb8156e0d08392c11040edc16b54a39682055a16c24b5d0831804790dad9d022100e50e74ce9cf312368f9c466fac10d3416ea7e0dd50148b912c659a0bf9b31baf0420433720f9cac23108d42719c96eb3b3516f7e4731d162a94128cc0fe5afe873690440d11f3e92d43c4096fe9743f370a4667b4b187c5cb1afa663c5e111c2de4c0b7fc5ef980b5b08c8a79f3be5e2163036a533613ece05da44a961bcd05f0fca833a
        //0484da95c5a77eaa2373cc6fbb316af1b0ea93a9edce6098e55004257c7b488e2b85b52401c81e1491d0453e9eb8798dca12bd5b632ac5b5a6602b4ff4c4812e6cce831a0bf4ac8be872643702676fcfa13068650c84c0faad68249f94edf69dc11bb950145caeb22e563ba0889ce770acad54abc36c6050329ea584797012a96cc5b8e3a799634b440177a48aacc7c14532d587634b6c93c130d9371ed2b6d69d

        //

        System.out.println(sm4Key);
    }

}

